package is.Hazir.SatGPS;

import java.util.ArrayList;

import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.util.Log;

/**
 * Provides access to a database of notes. Each note has a title, the note
 * itself, a creation date and a modified data.
 */
public class HazirDataProvider extends SQLiteOpenHelper {
	// Used for debugging and logging
	private static final String TAG = "HazirDataProvider";

	/**
	 * The database that the provider uses as its underlying data store
	 */
	private static final String DATABASE_NAME = "hazir.db";

	/**
	 * The database version
	 */
	private static final int DATABASE_VERSION = 3;

	public static final String TABLE_NAME_PLACES = "places";
	public static final String TABLE_NAME_VOICEMAIL = "voicemail_settings";

	public static final int PLACES_COL_NUM_ID = 0;
	public static final int PLACES_COL_NUM_LATITUDE = 1;
	public static final int PLACES_COL_NUM_LONGITUDE = 2;
	public static final int PLACES_COL_NUM_NAME = 3;
	public static final int PLACES_COL_NUM_SILENT = 4;
	public static final int PLACES_COL_NUM_VOICEMAIL = 5;
	public static final int PLACES_COL_NUM_RADIUS = 6;
	public static final int PLACES_COL_NUM_SMS = 7;
	public static final int PLACES_COL_NUM_SMS_TEXT = 8;

	public static final String PLACES_COL_NAME_ID = "_id";
	public static final String PLACES_COL_NAME_LATITUDE = "latitude";
	public static final String PLACES_COL_NAME_LONGITUDE = "longitude";
	public static final String PLACES_COL_NAME_NAME = "name";
	public static final String PLACES_COL_NAME_SILENT = "silent";
	public static final String PLACES_COL_NAME_VOICEMAIL = "voicemail";
	public static final String PLACES_COL_NAME_RADIUS = "radius";
	public static final String PLACES_COL_NAME_SMS = "sms";
	public static final String PLACES_COL_NAME_SMS_TEXT = "sms_text";

	public static final int VOICEMAIL_COL_NUM_ID = 0;
	public static final int VOICEMAIL_COL_NUM_SETTING = 1;
	public static final String VOICEMAIL_COL_NAME_ID = "raw_contact_id";
	public static final String VOICEMAIL_COL_NAME_SETTING = "send_to_voicemail";



	HazirDataProvider(Context context) {

		// calls the super constructor, requesting the default cursor factory.
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	/**
	 *
	 * Creates the underlying database.
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL("CREATE TABLE " + TABLE_NAME_PLACES + " ("
				+ PLACES_COL_NAME_ID + " INTEGER PRIMARY KEY,"
				+ PLACES_COL_NAME_LATITUDE + " FLOAT,"
				+ PLACES_COL_NAME_LONGITUDE + " FLOAT,"
				+ PLACES_COL_NAME_NAME + " TEXT,"
				+ PLACES_COL_NAME_SILENT + " BOOLEAN,"
				+ PLACES_COL_NAME_VOICEMAIL + " BOOLEAN,"
				+ PLACES_COL_NAME_RADIUS + " INTEGER,"
				+ PLACES_COL_NAME_SMS + " BOOLEAN,"
				+ PLACES_COL_NAME_SMS_TEXT + " TEXT"
				+ ");");
		db.execSQL("CREATE TABLE " + TABLE_NAME_VOICEMAIL + " ("
				+ VOICEMAIL_COL_NAME_ID + " INTEGER,"
				+ VOICEMAIL_COL_NAME_SETTING + " INTEGER"
				+ ");");
	}

	/**
	 *
	 * Upgrade the database
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

		// Logs that the database is being upgraded
		Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
				+ newVersion + ", which will destroy all old data");
		if (oldVersion < 2 && newVersion >= 2) {
			// Add radius column
			db.execSQL("ALTER TABLE " + TABLE_NAME_PLACES + " ADD COLUMN " + PLACES_COL_NAME_RADIUS + " INTEGER");
			db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME_VOICEMAIL + " ("
					+ VOICEMAIL_COL_NAME_ID + " INTEGER,"
					+ VOICEMAIL_COL_NAME_SETTING + " INTEGER"
					+ ");");
		}
		if (oldVersion < 3 && newVersion >= 3) {
			db.execSQL("ALTER TABLE " + TABLE_NAME_PLACES + " ADD COLUMN " + PLACES_COL_NAME_SMS + " BOOLEAN");
			db.execSQL("ALTER TABLE " + TABLE_NAME_PLACES + " ADD COLUMN " + PLACES_COL_NAME_SMS_TEXT + " TEXT");
		}
	}

	/**
	 * Gets a place from the database, based on either the latitude/longitude, ID or name.
	 */
	public Place get_place(int latitude, int longitude) {
		double fudge = Math.pow(Math.cos(Math.toRadians(latitude)), 2); // The skewing of the significance of longitudes at higher latitudes
		return place_query("", null, "((" + latitude + " - " + PLACES_COL_NAME_LATITUDE + ") * (" + latitude + " - " + PLACES_COL_NAME_LATITUDE + ") + (" + longitude + " - " + PLACES_COL_NAME_LONGITUDE + ") * (" + longitude + " - " + PLACES_COL_NAME_LONGITUDE + ") * " + fudge + ")");
	}

	public Place get_place(long id) {
		return place_query(PLACES_COL_NAME_ID + "=" + Long.toString(id), null, null);
	}

	public Place get_place(String name) {
		String[] selection_args = {name};
		return place_query(PLACES_COL_NAME_NAME + "=?", selection_args, null);
	}

	/**
	 * Gets all places in the database.
	 */
	public Cursor all_places() {
		SQLiteDatabase db = this.getReadableDatabase();
		String[] columns = {PLACES_COL_NAME_ID, PLACES_COL_NAME_LATITUDE, PLACES_COL_NAME_LONGITUDE, PLACES_COL_NAME_NAME, PLACES_COL_NAME_SILENT, PLACES_COL_NAME_VOICEMAIL, PLACES_COL_NAME_RADIUS, PLACES_COL_NAME_SMS, PLACES_COL_NAME_SMS_TEXT};
		Cursor places = db.query(TABLE_NAME_PLACES, columns, "", null, null, null, PLACES_COL_NAME_NAME);	
		return places;
	}

	/**
	 * Stores a place in the database.
	 * If the place has the id property set to an existing ID, update the data for that place.
	 * Otherwise, create a new place.
	 * Return the place's ID (mostly useful when creating a new place).
	 */
	public long store_place(Place place) throws SQLException {
		SQLiteDatabase db = this.getWritableDatabase();
		ContentValues values = new ContentValues();
		values.put(PLACES_COL_NAME_LATITUDE, place.latitude);
		values.put(PLACES_COL_NAME_LONGITUDE, place.longitude);
		values.put(PLACES_COL_NAME_NAME, place.name);
		values.put(PLACES_COL_NAME_SILENT, place.silent);
		values.put(PLACES_COL_NAME_VOICEMAIL, place.voicemail);
		values.put(PLACES_COL_NAME_RADIUS, place.radius);
		values.put(PLACES_COL_NAME_SMS, place.sms);
		values.put(PLACES_COL_NAME_SMS_TEXT, place.smsText);
		if (place.id > 0) {
			// Place exists
			db.update(TABLE_NAME_PLACES, values, PLACES_COL_NAME_ID + "=" + Long.toString(place.id), null);
		}
		else {
			// Create a new place
			place.id = db.insert(TABLE_NAME_PLACES, null, values);
		}
		db.close();
		return place.id;
	}

	/**
	 * Deletes a place with a certain ID from the database.
	 * @param place
	 */
	public void delete_place(long id) throws SQLException {
		SQLiteDatabase db = this.getWritableDatabase();
		db.delete(TABLE_NAME_PLACES, PLACES_COL_NAME_ID + "=" + Long.toString(id), null);
		db.close();
	}

	/**
	 * Queries the database for a place based on the given where parameters and populates a Place object.
	 */
	private Place place_query(String where_clause, String[] selection_args, String orderBy) {
		SQLiteDatabase db = this.getReadableDatabase();

		String[] columns = {PLACES_COL_NAME_ID, PLACES_COL_NAME_LATITUDE, PLACES_COL_NAME_LONGITUDE, PLACES_COL_NAME_NAME, PLACES_COL_NAME_SILENT, PLACES_COL_NAME_VOICEMAIL, PLACES_COL_NAME_RADIUS, PLACES_COL_NAME_SMS, PLACES_COL_NAME_SMS_TEXT};
		if (orderBy == null) {
			orderBy = PLACES_COL_NAME_ID;
		}
		Cursor cursor = db.query(TABLE_NAME_PLACES, columns, where_clause, selection_args, null, null, orderBy);
		Place place = new Place();
		if (cursor.getCount() > 0) {
			// There is a place matching this query
			cursor.moveToNext();
			place.id 		= cursor.getLong(PLACES_COL_NUM_ID);
			place.latitude 	= cursor.getInt(PLACES_COL_NUM_LATITUDE);
			place.longitude = cursor.getInt(PLACES_COL_NUM_LONGITUDE);
			place.name 		= cursor.getString(PLACES_COL_NUM_NAME);
			place.silent 	= cursor.getInt(PLACES_COL_NUM_SILENT) > 0; // To get boolean from int
			place.voicemail = cursor.getInt(PLACES_COL_NUM_VOICEMAIL) > 0; // To get boolean from int
			place.radius 	= cursor.getInt(PLACES_COL_NUM_RADIUS);
			place.sms 		= cursor.getInt(PLACES_COL_NUM_SMS) > 0; //To get boolean from int
			place.smsText 	= cursor.getString(PLACES_COL_NUM_SMS_TEXT);
			cursor.close();
			db.close();
			return place;
		}
		else {
			cursor.close();
			db.close();
			return null;
		}
	}
	
	/**
	 * Stores a backup of the user's voicemail settings for each of his/her raw contacts.
	 */
	public void dump_contacts(Context context) throws SQLException {
		SQLiteDatabase db = this.getWritableDatabase();
		ContentResolver cr = context.getContentResolver();
		Cursor cur = cr.query(RawContacts.CONTENT_URI, null, null, null, null);
		if (cur.getCount() > 0) {
			while (cur.moveToNext()) {
				ContentValues values = new ContentValues();
				values.put(VOICEMAIL_COL_NAME_ID, cur.getString(cur.getColumnIndex(RawContacts._ID)));
				values.put(VOICEMAIL_COL_NAME_SETTING, cur.getInt(cur.getColumnIndex(RawContacts.SEND_TO_VOICEMAIL)));
				db.insert(TABLE_NAME_VOICEMAIL, null, values);
			}
		}
		cur.close();
		db.close();
	}

	/**
	 * Loads a dumped backup back into the phone's contacts.
	 * @throws OperationApplicationException 
	 * @throws RemoteException 
	 */
	public void restore_contacts(Context context) throws SQLException, RemoteException, OperationApplicationException {
		SQLiteDatabase db = this.getWritableDatabase();
		ContentResolver cr = context.getContentResolver();
		String[] columns = {VOICEMAIL_COL_NAME_ID, VOICEMAIL_COL_NAME_SETTING};
		Cursor cursor = db.query(TABLE_NAME_VOICEMAIL, columns, "", null, null, null, null);
		ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();

		if (cursor.getCount() > 0) {
			while (cursor.moveToNext()) {
				ops.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI).withSelection(RawContacts._ID + "=" + cursor.getInt(VOICEMAIL_COL_NUM_ID), null).withValue(RawContacts.SEND_TO_VOICEMAIL, cursor.getInt(VOICEMAIL_COL_NUM_SETTING)).build());
			}
		}
		cursor.close();
		cr.applyBatch(ContactsContract.AUTHORITY, ops);
		db.delete(TABLE_NAME_VOICEMAIL, null, null);
		db.close();
	}
}
